package zhanglinwei.cryptotools.core.processor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import zhanglinwei.cryptotools.core.constant.CryptoToolsConstant;
import zhanglinwei.cryptotools.core.resolver.CryptoablePropertyResolverFactory;
import zhanglinwei.cryptotools.core.source.CacheCryptoablePropertySource;
import zhanglinwei.cryptotools.core.source.CryptoablePropertySource;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class EnableCryptoPropertyBeanFactoryPostProcessor implements BeanFactoryPostProcessor, Ordered {
    private static final Logger log = LoggerFactory.getLogger(EnableCryptoPropertyBeanFactoryPostProcessor.class);
    private static final List<String> DEFAULT_SKIP_PROPERTY_SOURCE_CLASSES = Arrays.asList(
            "org.springframework.core.env.PropertySource$StubPropertySource",
            "org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertySource"
    );

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        log.info("EnableCryptoPropertyBeanFactoryPostProcessor execute postProcessBeanFactory...");
        ConfigurableEnvironment environment = beanFactory.getBean(ConfigurableEnvironment.class);
        MutablePropertySources propertySources = environment.getPropertySources();
        StreamSupport.stream(propertySources.spliterator(), false) // false 表示顺序流
                .filter(item -> !(item instanceof CryptoablePropertySource))
                .map(item -> makeCryptoable(item, beanFactory, environment))
                .collect(Collectors.toList())
                .forEach(item -> propertySources.replace(item.getName(), item));
    }

    private <T> PropertySource<T> makeCryptoable(PropertySource<T> propertySource, BeanFactory beanFactory, Environment environment) {
        if (propertySource instanceof CryptoablePropertySource || DEFAULT_SKIP_PROPERTY_SOURCE_CLASSES.stream().anyMatch(skipClass -> skipClass.equals(propertySource.getClass().getName()))) {
            return propertySource;
        }
        String currentAlgorithm = environment.getProperty(CryptoToolsConstant.ALGORITHM_KEY, String.class, CryptoToolsConstant.DEFAULT_ALGORITHM);
        CryptoablePropertyResolverFactory resolverFactory = beanFactory.getBean(CryptoablePropertyResolverFactory.class);
//        PropertySource<T> cryptoablePropertySource;
//        if (propertySource instanceof SystemEnvironmentPropertySource) {
//            cryptoablePropertySource = (PropertySource<T>) new CryptoableSystemEnvironmentPropertySourceWrapper((SystemEnvironmentPropertySource) propertySource, propertyResolver);
//        } else if (propertySource instanceof MapPropertySource) {
//            cryptoablePropertySource = (PropertySource<T>) new CryptoableMapPropertySourceWrapper((MapPropertySource) propertySource, propertyResolver);
//        } else if (propertySource instanceof EnumerablePropertySource) {
//            cryptoablePropertySource = new CryptoableEnumerablePropertySourceWrapper<>((EnumerablePropertySource) propertySource, propertyResolver);
//        } else {
//            cryptoablePropertySource = new CryptoablePropertySourceWrapper<>(propertySource, propertyResolver);
//        }
//        return cryptoablePropertySource;
        return new CacheCryptoablePropertySource<>(propertySource, resolverFactory.getPropertyResolver(currentAlgorithm));
    }

    @Override
    public int getOrder() {
        return LOWEST_PRECEDENCE;
    }
}
